package com.laamella.syntherface.synths.midi;

import java.util.ArrayList;
import java.util.List;

import javax.sound.midi.SysexMessage;

import com.laamella.syntherface.synths.models.ParameterModel;

/**
 * BinaryView maps {@link ParameterModel}s or static values to an array of
 * bytes in various ways. This can be used to encode or decode a
 * {@link SysexMessage}.
 * 
 * <p>
 * This can/should be subclassed for specific synthesizers, at least to add
 * checksum functionality.
 * 
 * @author danny
 */
public abstract class BinaryData {

	private int size;
	public final List<BinaryField> binaryParameters = new ArrayList<BinaryField>();

	/**
	 * Create a binary view that auto-calculates its size.
	 */
	private BinaryData() {
		// TODO not implemented
	}

	/**
	 * Create a binary view with a static size.
	 * 
	 * @param size
	 */
	public BinaryData(int size) {
		this.size = size;
	}

	public final byte[] encode() throws Exception {
		byte[] data = new byte[size];
		data = preEncodeParameters(data);
		data = encodeParameters(data);
		return postEncodeParameters(data);
	}

	/**
	 * A good place to do checksum calculations.
	 * 
	 * @param data
	 *                the data currently being assembled.
	 */
	protected byte[] postEncodeParameters(byte[] data) throws Exception {
		return data;
	}

	/**
	 * Goes through the list of registered parameters, encoding them as
	 * binary data in data.
	 * 
	 * @param data
	 * @throws Exception
	 */
	protected byte[] encodeParameters(byte[] data) throws Exception {
		for (BinaryField parameter : binaryParameters) {
			parameter.encode(data);
		}
		return data;
	}

	protected byte[] preEncodeParameters(byte[] data) throws Exception {
		return data;
	}

	public final void decode(byte[] data) throws Exception {
		data = preDecodeParameters(data);
		data = decodeParameters(data);
		postDecodeParameters(data);
	}

	protected void postDecodeParameters(byte[] data) throws Exception {
	}

	protected byte[] decodeParameters(byte[] data) throws Exception {
		for (BinaryField parameter : binaryParameters) {
			parameter.decode(data);
		}
		return data;
	}

	/**
	 * A good place to do some sanity checks, like checksum calculations.
	 * 
	 * @param data
	 *                the data, probably extracted from a
	 *                {@link SysexMessage}.
	 */
	protected byte[] preDecodeParameters(byte[] data) throws Exception {
		return data;
	}

}
